/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *  Copyright 2005, 2006 by
 *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:  Heads-up displays
 *
 *-----------------------------------------------------------------------------
 */

// killough 5/3/98: remove unnecessary headers

#include "doomstat.h"
#include "hu_stuff.h"
#include "hu_lib.h"
#include "hu_tracers.h"
#include "st_stuff.h" /* jff 2/16/98 need loc of status bar */
#include "w_wad.h"
#include "s_sound.h"
#include "dstrings.h"
#include "sounds.h"
#include "d_deh.h"   /* Ty 03/27/98 - externalization of mapnamesx arrays */
#include "g_game.h"
#include "r_main.h"
#include "p_inter.h"
#include "lprintf.h"
#include "e6y.h" //e6y

// global heads up display controls

int hud_active;       //jff 2/17/98 controls heads-up display mode
int hud_displayed;    //jff 2/23/98 turns heads-up display on/off
int hud_distributed;  //jff 3/4/98 display HUD in different places on screen

//
// Locally used constants, shortcuts.
//
// Ty 03/28/98 -
// These four shortcuts modifed to reflect char ** of mapnamesx[]
// e6y: why sizeof(mapnamest)/sizeof(mapnamest[0]) does not work?
#define HU_TITLE  (*mapnames[(gameepisode-1)*9+gamemap-1])
#define HU_TITLE2 (gamemap <= 32 ? *mapnames2[gamemap-1] : "")
#define HU_TITLEP (gamemap <= 32 ? *mapnamesp[gamemap-1] : "")
#define HU_TITLET (gamemap <= 32 ? *mapnamest[gamemap-1] : "")
#define HU_TITLEHEIGHT  1
#define HU_TITLEX 0
//jff 2/16/98 change 167 to ST_Y-1
// CPhipps - changed to ST_TY
// proff - changed to 200-ST_HEIGHT for stretching
#define HU_TITLEY ((200-ST_HEIGHT) - 1 - hu_font[0].height)

//jff 2/16/98 add coord text widget coordinates
// proff - changed to SCREENWIDTH to 320 for stretching
#define HU_COORDX (320 - 13*hu_font2['A'-HU_FONTSTART].width)
//jff 3/3/98 split coord widget into three lines in upper right of screen
#define HU_COORDXYZ_Y (1 * hu_font['A'-HU_FONTSTART].height + 1)
#define HU_COORDX_Y (0 + 0*hu_font['A'-HU_FONTSTART].height + HU_COORDXYZ_Y)
#define HU_COORDY_Y (1 + 1*hu_font['A'-HU_FONTSTART].height + HU_COORDXYZ_Y)
#define HU_COORDZ_Y (2 + 2*hu_font['A'-HU_FONTSTART].height + HU_COORDXYZ_Y)

#define HU_MAP_STAT_X (0)
#define HU_MAP_STAT_Y (1 * hu_font['A'-HU_FONTSTART].height + 1)
#define HU_MAP_MONSTERS_Y  (0 + 0*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y)
#define HU_MAP_SECRETS_Y   (1 + 1*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y)
#define HU_MAP_ITEMS_Y     (2 + 2*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y)
#define HU_MAP_TIME_Y      (4 + 4*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y)
#define HU_MAP_TOTALTIME_Y (5 + 5*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y)

//jff 2/16/98 add ammo, health, armor widgets, 2/22/98 less gap
#define HU_GAPY 8
#define HU_HUDHEIGHT (6*HU_GAPY)
#define HU_HUDX 2
#define HU_HUDY (200-HU_HUDHEIGHT-1)
#define HU_MONSECX (HU_HUDX)
#define HU_MONSECY (HU_HUDY+0*HU_GAPY)
#define HU_KEYSX   (HU_HUDX)
//jff 3/7/98 add offset for graphic key widget
#define HU_KEYSGX  (HU_HUDX+4*hu_font2['A'-HU_FONTSTART].width)
#define HU_KEYSY   (HU_HUDY+1*HU_GAPY)
#define HU_WEAPX   (HU_HUDX)
#define HU_WEAPY   (HU_HUDY+2*HU_GAPY)
#define HU_AMMOX   (HU_HUDX)
#define HU_AMMOY   (HU_HUDY+3*HU_GAPY)
#define HU_HEALTHX (HU_HUDX)
#define HU_HEALTHY (HU_HUDY+4*HU_GAPY)
#define HU_ARMORX  (HU_HUDX)
#define HU_ARMORY  (HU_HUDY+5*HU_GAPY)

//jff 3/4/98 distributed HUD positions
#define HU_HUDX_LL 2
#define HU_HUDY_LL (200-2*HU_GAPY-1)
// proff/nicolas 09/20/98: Changed for high-res
#define HU_HUDX_LR (320-112)
#define HU_HUDY_LR (200-2*HU_GAPY-1)
// proff/nicolas 09/20/98: Changed for high-res
#define HU_HUDX_UR (320-96)
#define HU_HUDY_UR 2
#define HU_MONSECX_D (HU_HUDX_LL)
#define HU_MONSECY_D (HU_HUDY_LL+0*HU_GAPY)
#define HU_KEYSX_D   (HU_HUDX_LL)
#define HU_KEYSGX_D  (HU_HUDX_LL+4*hu_font2['A'-HU_FONTSTART].width)
#define HU_KEYSY_D   (HU_HUDY_LL+1*HU_GAPY)
#define HU_WEAPX_D   (HU_HUDX_LR)
#define HU_WEAPY_D   (HU_HUDY_LR+0*HU_GAPY)
#define HU_AMMOX_D   (HU_HUDX_LR)
#define HU_AMMOY_D   (HU_HUDY_LR+1*HU_GAPY)
#define HU_HEALTHX_D (HU_HUDX_UR)
#define HU_HEALTHY_D (HU_HUDY_UR+0*HU_GAPY)
#define HU_ARMORX_D  (HU_HUDX_UR)
#define HU_ARMORY_D  (HU_HUDY_UR+1*HU_GAPY)

//#define HU_INPUTTOGGLE  't' // not used                           // phares
#define HU_INPUTX HU_MSGX
#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(hu_font[0].height) +1)
#define HU_INPUTWIDTH 64
#define HU_INPUTHEIGHT  1

#define HU_TRACERX (2)
#define HU_TRACERY (hu_font['A'-HU_FONTSTART].height)

#define key_alt KEYD_RALT
#define key_shift KEYD_RSHIFT

const char* chat_macros[] =
// Ty 03/27/98 - *not* externalized
// CPhipps - const char*
{
  HUSTR_CHATMACRO0,
  HUSTR_CHATMACRO1,
  HUSTR_CHATMACRO2,
  HUSTR_CHATMACRO3,
  HUSTR_CHATMACRO4,
  HUSTR_CHATMACRO5,
  HUSTR_CHATMACRO6,
  HUSTR_CHATMACRO7,
  HUSTR_CHATMACRO8,
  HUSTR_CHATMACRO9
};

const char* player_names[] =
// Ty 03/27/98 - *not* externalized
// CPhipps - const char*
{
  HUSTR_PLRGREEN,
  HUSTR_PLRINDIGO,
  HUSTR_PLRBROWN,
  HUSTR_PLRRED
};

//jff 3/17/98 translate player colmap to text color ranges
int plyrcoltran[MAXPLAYERS]={CR_GREEN,CR_GRAY,CR_BROWN,CR_RED};

char chat_char;                 // remove later.
static player_t*  plr;

// font sets
patchnum_t hu_font[HU_FONTSIZE];
patchnum_t hu_font2[HU_FONTSIZE];
patchnum_t hu_fontk[HU_FONTSIZE];//jff 3/7/98 added for graphic key indicators
patchnum_t hu_msgbg[9];          //jff 2/26/98 add patches for message background
patchnum_t hu_font_hud[HU_FONTSIZE];

// widgets
static hu_textline_t  w_title;
static hu_stext_t     w_message;
static hu_itext_t     w_chat;
static hu_itext_t     w_inputbuffer[MAXPLAYERS];
static hu_textline_t  w_coordx; //jff 2/16/98 new coord widget for automap
static hu_textline_t  w_coordy; //jff 3/3/98 split coord widgets automap
static hu_textline_t  w_coordz; //jff 3/3/98 split coord widgets automap
static hu_textline_t  w_ammo;   //jff 2/16/98 new ammo widget for hud
static hu_textline_t  w_health; //jff 2/16/98 new health widget for hud
static hu_textline_t  w_armor;  //jff 2/16/98 new armor widget for hud
static hu_textline_t  w_weapon; //jff 2/16/98 new weapon widget for hud
static hu_textline_t  w_keys;   //jff 2/16/98 new keys widget for hud
static hu_textline_t  w_gkeys;  //jff 3/7/98 graphic keys widget for hud
static hu_textline_t  w_monsec; //jff 2/16/98 new kill/secret widget for hud
static hu_mtext_t     w_rtext;  //jff 2/26/98 text message refresh widget

static hu_textline_t  w_map_monsters;  //e6y monsters widget for automap
static hu_textline_t  w_map_secrets;   //e6y secrets widgets automap
static hu_textline_t  w_map_items;     //e6y items widgets automap
static hu_textline_t  w_map_time;      //e6y level time widgets automap
static hu_textline_t  w_map_totaltime; //e6y total time widgets automap

static hu_textline_t  w_health_big;
static hu_textline_t  w_health_medict;
static hu_textline_t  w_armor_big;
static hu_textline_t  w_armor_armor;

static dboolean    always_off = false;
static char       chat_dest[MAXPLAYERS];
dboolean           chat_on;
static dboolean    message_on;
static dboolean    message_list; //2/26/98 enable showing list of messages
dboolean           message_dontfuckwithme;
static dboolean    message_nottobefuckedwith;
static int        message_counter;
extern int        showMessages;
extern dboolean    automapactive;
static dboolean    headsupactive = false;

//jff 2/16/98 hud supported automap colors added
int hudcolor_titl;  // color range of automap level title
int hudcolor_xyco;  // color range of new coords on automap
int hudcolor_mapstat_title;
int hudcolor_mapstat_value;
int hudcolor_mapstat_time;
//jff 2/16/98 hud text colors, controls added
int hudcolor_mesg;  // color range of scrolling messages
int hudcolor_chat;  // color range of chat lines
int hud_msg_lines;  // number of message lines in window
//jff 2/26/98 hud text colors, controls added
int hudcolor_list;  // list of messages color
int hud_list_bgon;  // enable for solid window background for message list

//jff 2/16/98 initialization strings for ammo, health, armor widgets
static char hud_coordstrx[32];
static char hud_coordstry[32];
static char hud_coordstrz[32];
static char hud_ammostr[80];
static char hud_healthstr[80];
static char hud_armorstr[80];
static char hud_weapstr[80];
static char hud_keysstr[80];
static char hud_gkeysstr[80]; //jff 3/7/98 add support for graphic key display
static char hud_monsecstr[80];

//
// Builtin map names.
// The actual names can be found in DStrings.h.
//
// Ty 03/27/98 - externalized map name arrays - now in d_deh.c
// and converted to arrays of pointers to char *
// See modified HUTITLEx macros
extern char **mapnames[];
extern char **mapnames2[];
extern char **mapnamesp[];
extern char **mapnamest[];

extern int map_point_coordinates;
extern int map_level_stat;

// key tables
// jff 5/10/98 french support removed,
// as it was not being used and couldn't be easily tested
//
const char* shiftxform;

const char english_shiftxform[] =
{
  0,
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
  31,
  ' ', '!', '"', '#', '$', '%', '&',
  '"', // shift-'
  '(', ')', '*', '+',
  '<', // shift-,
  '_', // shift--
  '>', // shift-.
  '?', // shift-/
  ')', // shift-0
  '!', // shift-1
  '@', // shift-2
  '#', // shift-3
  '$', // shift-4
  '%', // shift-5
  '^', // shift-6
  '&', // shift-7
  '*', // shift-8
  '(', // shift-9
  ':',
  ':', // shift-;
  '<',
  '+', // shift-=
  '>', '?', '@',
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
  'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  '[', // shift-[
  '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
  ']', // shift-]
  '"', '_',
  '\'', // shift-`
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
  'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  '{', '|', '}', '~', 127
};

//
// HU_Init()
//
// Initialize the heads-up display, text that overwrites the primary display
//
// Passed nothing, returns nothing
//
void HU_Init(void)
{

  int   i;
  int   j;
  char  buffer[9];

  shiftxform = english_shiftxform;

  // load the heads-up font
  j = HU_FONTSTART;
  for (i=0;i<HU_FONTSIZE;i++,j++)
  {
    if ('0'<=j && j<='9')
    {
      sprintf(buffer, "DIG%.1d",j-48);
      R_SetPatchNum(&hu_font2[i], buffer);
      sprintf(buffer, "STCFN%.3d",j);
      R_SetPatchNum(&hu_font[i], buffer);
      sprintf(buffer, "STTNUM%.1d",j-48);
      R_SetPatchNum(&hu_font_hud[i], buffer);
    }
    else if ('A'<=j && j<='Z')
    {
      sprintf(buffer, "DIG%c",j);
      R_SetPatchNum(&hu_font2[i], buffer);
      sprintf(buffer, "STCFN%.3d",j);
      R_SetPatchNum(&hu_font[i], buffer);
    }
    else if (j=='-')
    {
      R_SetPatchNum(&hu_font2[i], "DIG45");
      R_SetPatchNum(&hu_font[i], "STCFN045");
    }
    else if (j=='/')
    {
      R_SetPatchNum(&hu_font2[i], "DIG47");
      R_SetPatchNum(&hu_font[i], "STCFN047");
    }
    else if (j==':')
    {
      R_SetPatchNum(&hu_font2[i], "DIG58");
      R_SetPatchNum(&hu_font[i], "STCFN058");
    }
    else if (j=='[')
    {
      R_SetPatchNum(&hu_font2[i], "DIG91");
      R_SetPatchNum(&hu_font[i], "STCFN091");
    }
    else if (j==']')
    {
      R_SetPatchNum(&hu_font2[i], "DIG93");
      R_SetPatchNum(&hu_font[i], "STCFN093");
    }
    else if (j<97)
    {
      sprintf(buffer, "STCFN%.3d",j);
      R_SetPatchNum(&hu_font2[i], buffer);
      R_SetPatchNum(&hu_font[i], buffer);
      //jff 2/23/98 make all font chars defined, useful or not
    }
    else if (j>122)
    {
      sprintf(buffer, "STBR%.3d",j);
      R_SetPatchNum(&hu_font2[i], buffer);
      R_SetPatchNum(&hu_font[i], buffer);
    }
    else
      hu_font[i] = hu_font[0]; //jff 2/16/98 account for gap
  }

  // CPhipps - load patches for message background
  for (i=0; i<9; i++) {
    sprintf(buffer, "BOX%c%c", "UCL"[i/3], "LCR"[i%3]);
    R_SetPatchNum(&hu_msgbg[i], buffer);
  }

  // CPhipps - load patches for keys and double keys
  for (i=0; i<6; i++) {
    sprintf(buffer, "STKEYS%d", i);
    R_SetPatchNum(&hu_fontk[i], buffer);
  }

  R_SetSpriteByIndex(&hu_font_hud[4], SPR_MEDI);
  R_SetSpriteByIndex(&hu_font_hud[5], SPR_ARM1);
  R_SetSpriteByIndex(&hu_font_hud[6], SPR_ARM1);
  R_SetSpriteByIndex(&hu_font_hud[7], SPR_ARM2);

  R_SetSpriteByIndex(&hu_font_hud[8], SPR_STIM);
  R_SetSpriteByName(&hu_font_hud[9], "BON2A0");
  R_SetSpriteByName(&hu_font_hud[10], "BON2B0");
  R_SetSpriteByName(&hu_font_hud[11], "BON2D0");
}

//
// HU_Stop()
//
// Make the heads-up displays inactive
//
// Passed nothing, returns nothing
//
static void HU_Stop(void)
{
  headsupactive = false;
}

//
// HU_Start(void)
//
// Create and initialize the heads-up widgets, software machines to
// maintain, update, and display information over the primary display
//
// This routine must be called after any change to the heads up configuration
// in order for the changes to take effect in the actual displays
//
// Passed nothing, returns nothing
//
void HU_Start(void)
{
  int   i;
  const char* s; /* cph - const */

  if (headsupactive)                    // stop before starting
    HU_Stop();

  plr = &players[displayplayer];        // killough 3/7/98
  message_on = false;
  message_dontfuckwithme = false;
  message_nottobefuckedwith = false;
  chat_on = false;

  // create the message widget
  // messages to player in upper-left of screen
  HUlib_initSText
  (
    &w_message,
    HU_MSGX,
    HU_MSGY,
    HU_MSGHEIGHT,
    hu_font,
    HU_FONTSTART,
    hudcolor_mesg,
    VPT_ALIGN_LEFT_TOP,
    &message_on
  );

  //jff 2/16/98 added some HUD widgets
  // create the map title widget - map title display in lower left of automap
  HUlib_initTextLine
  (
    &w_title,
    HU_TITLEX,
    HU_TITLEY,
    hu_font,
    HU_FONTSTART,
    hudcolor_titl,
    VPT_ALIGN_LEFT_BOTTOM
  );

  // create the hud health widget
  // bargraph and number for amount of health,
  // lower left or upper right of screen
  HUlib_initTextLine
  (
    &w_health,
    hud_distributed? HU_HEALTHX_D : HU_HEALTHX,  //3/4/98 distribute
    hud_distributed? HU_HEALTHY_D : HU_HEALTHY,
    hu_font2,
    HU_FONTSTART,
    CR_GREEN,
    VPT_NONE
  );

  HUlib_initTextLine
  (
    &w_health_big,
    0,
    0,
    hu_font_hud,
    HU_FONTSTART,
    CR_RED,
    VPT_NONE
  );

  HUlib_initTextLine
  (
    &w_health_medict,
    0,
    0,
    hu_font_hud,
    HU_FONTSTART,
    CR_RED,
    VPT_NONE
  );

  // create the hud armor widget
  // bargraph and number for amount of armor,
  // lower left or upper right of screen
  HUlib_initTextLine
  (
    &w_armor,
    hud_distributed? HU_ARMORX_D : HU_ARMORX,    //3/4/98 distribute
    hud_distributed? HU_ARMORY_D : HU_ARMORY,
    hu_font2,
    HU_FONTSTART,
    CR_GREEN,
    VPT_NONE
  );

  HUlib_initTextLine
  (
    &w_armor_big,
    0,
    0,
    hu_font_hud,
    HU_FONTSTART,
    CR_RED,
    VPT_NONE
  );

  HUlib_initTextLine
  (
    &w_armor_armor,
    0,
    0,
    hu_font_hud,
    HU_FONTSTART,
    CR_RED,
    VPT_NONE
  );

  // create the hud ammo widget
  // bargraph and number for amount of ammo for current weapon,
  // lower left or lower right of screen
  HUlib_initTextLine
  (
    &w_ammo,
    hud_distributed? HU_AMMOX_D : HU_AMMOX,      //3/4/98 distribute
    hud_distributed? HU_AMMOY_D : HU_AMMOY,
    hu_font2,
    HU_FONTSTART,
    CR_GOLD,
    VPT_NONE
  );

  // create the hud weapons widget
  // list of numbers of weapons possessed
  // lower left or lower right of screen
  HUlib_initTextLine
  (
    &w_weapon,
    hud_distributed? HU_WEAPX_D : HU_WEAPX,      //3/4/98 distribute
    hud_distributed? HU_WEAPY_D : HU_WEAPY,
    hu_font2,
    HU_FONTSTART,
    CR_GRAY,
    VPT_NONE
  );

  // create the hud keys widget
  // display of key letters possessed
  // lower left of screen
  HUlib_initTextLine
  (
    &w_keys,
    hud_distributed? HU_KEYSX_D : HU_KEYSX,      //3/4/98 distribute
    hud_distributed? HU_KEYSY_D : HU_KEYSY,
    hu_font2,
    HU_FONTSTART,
    CR_GRAY,
    VPT_NONE
  );

  // create the hud graphic keys widget
  // display of key graphics possessed
  // lower left of screen
  HUlib_initTextLine
  (
    &w_gkeys,
    hud_distributed? HU_KEYSGX_D : HU_KEYSGX,    //3/4/98 distribute
    hud_distributed? HU_KEYSY_D : HU_KEYSY,
    hu_fontk,
    HU_FONTSTART,
    CR_RED,
    VPT_NONE
  );

  // create the hud monster/secret widget
  // totals and current values for kills, items, secrets
  // lower left of screen
  HUlib_initTextLine
  (
    &w_monsec,
    hud_distributed? HU_MONSECX_D : HU_MONSECX,  //3/4/98 distribute
    hud_distributed? HU_MONSECY_D : HU_MONSECY,
    hu_font2,
    HU_FONTSTART,
    CR_GRAY,
    VPT_NONE
  );

  // create the hud text refresh widget
  // scrolling display of last hud_msg_lines messages received
  if (hud_msg_lines>HU_MAXMESSAGES)
    hud_msg_lines=HU_MAXMESSAGES;
  //jff 4/21/98 if setup has disabled message list while active, turn it off
  message_list = hud_msg_lines > 1; //jff 8/8/98 initialize both ways
  //jff 2/26/98 add the text refresh widget initialization
  HUlib_initMText
  (
    &w_rtext,
    0,
    0,
    320,
//    SCREENWIDTH,
    (hud_msg_lines+2)*HU_REFRESHSPACING,
    hu_font,
    HU_FONTSTART,
    hudcolor_list,
    hu_msgbg,
    VPT_ALIGN_LEFT_TOP,
    &message_list
  );

  // initialize the automap's level title widget
  // e6y: stop SEGV here when gamemap is not initialized
  if (gamestate == GS_LEVEL && gamemap > 0) /* cph - stop SEGV here when not in level */
  switch (gamemode)
  {
    case shareware:
    case registered:
    case retail:
      s = HU_TITLE;
      break;

    case commercial:
    default:  // Ty 08/27/98 - modified to check mission for TNT/Plutonia
      s = (gamemission==pack_tnt)  ? HU_TITLET :
          (gamemission==pack_plut) ? HU_TITLEP : HU_TITLE2;
      break;
  } else s = "";
  while (*s)
    HUlib_addCharToTextLine(&w_title, *(s++));

  // create the automaps coordinate widget
  // jff 3/3/98 split coord widget into three lines: x,y,z
  // jff 2/16/98 added
  HUlib_initTextLine
  (
    &w_coordx,
    HU_COORDX,
    HU_COORDX_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_xyco,
    VPT_ALIGN_RIGHT_TOP
  );
  HUlib_initTextLine
  (
    &w_coordy,
    HU_COORDX,
    HU_COORDY_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_xyco,
    VPT_ALIGN_RIGHT_TOP
  );
  HUlib_initTextLine
  (
    &w_coordz,
    HU_COORDX,
    HU_COORDZ_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_xyco,
    VPT_ALIGN_RIGHT_TOP
  );
//e6y
  HUlib_initTextLine
  (
    &w_map_monsters,
    HU_MAP_STAT_X,
    HU_MAP_MONSTERS_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_mapstat_title,
    VPT_ALIGN_LEFT_TOP
  );
  HUlib_initTextLine
  (
    &w_map_secrets,
    HU_MAP_STAT_X,
    HU_MAP_SECRETS_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_mapstat_title,
    VPT_ALIGN_LEFT_TOP
  );
  HUlib_initTextLine
  (
    &w_map_items,
    HU_MAP_STAT_X,
    HU_MAP_ITEMS_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_mapstat_title,
    VPT_ALIGN_LEFT_TOP
  );
  HUlib_initTextLine
  (
    &w_map_time,
    HU_MAP_STAT_X,
    HU_MAP_TIME_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_mapstat_time,
    VPT_ALIGN_LEFT_TOP
  );
  HUlib_initTextLine
  (
    &w_map_totaltime,
    HU_MAP_STAT_X,
    HU_MAP_TOTALTIME_Y,
    hu_font,
    HU_FONTSTART,
    hudcolor_mapstat_time,
    VPT_ALIGN_LEFT_TOP
  );
  HUlib_initTextLine
  (
    &w_hudadd,
    hud_distributed? HU_HUDADDX_D : HU_HUDADDX,  //3/4/98 distribute
    hud_distributed? HU_HUDADDY_D : HU_HUDADDY,
    hu_font2,
    HU_FONTSTART,
    CR_GRAY,
    VPT_NONE
  );
  HUlib_initTextLine
  (
    &w_centermsg,
    HU_CENTERMSGX,
    HU_CENTERMSGY,
    hu_font,
    HU_FONTSTART,
    hudcolor_titl,
    VPT_STRETCH
  );
  HUlib_initTextLine
  (
    &w_precache,
    16,
    186,
    hu_font,
    HU_FONTSTART,
    CR_RED,
    VPT_ALIGN_LEFT_BOTTOM
  );
  strcpy(hud_add,"");
  s = hud_add;
  while (*s)
    HUlib_addCharToTextLine(&w_hudadd, *(s++));

  for(i = 0; i < NUMTRACES; i++)
  {
    HUlib_initTextLine(
      &w_traces[i],
      HU_TRACERX,
      HU_TRACERY+i*HU_GAPY,
      hu_font2,
      HU_FONTSTART,
      CR_GRAY,
      VPT_ALIGN_LEFT_BOTTOM
    );

    strcpy(traces[i].hudstr, "");
    s = traces[i].hudstr;
    while (*s)
      HUlib_addCharToTextLine(&w_traces[i], *(s++));
    HUlib_drawTextLine(&w_traces[i], false);
  }


  // initialize the automaps coordinate widget
  //jff 3/3/98 split coordstr widget into 3 parts
  if (map_point_coordinates)
  {
    sprintf(hud_coordstrx,"X: %-5d",0); //jff 2/22/98 added z
    s = hud_coordstrx;
    while (*s)
      HUlib_addCharToTextLine(&w_coordx, *(s++));
    sprintf(hud_coordstry,"Y: %-5d",0); //jff 3/3/98 split x,y,z
    s = hud_coordstry;
    while (*s)
      HUlib_addCharToTextLine(&w_coordy, *(s++));
    sprintf(hud_coordstrz,"Z: %-5d",0); //jff 3/3/98 split x,y,z
    s = hud_coordstrz;
    while (*s)
      HUlib_addCharToTextLine(&w_coordz, *(s++));
  }

  //jff 2/16/98 initialize ammo widget
  strcpy(hud_ammostr,"AMM ");
  s = hud_ammostr;
  while (*s)
    HUlib_addCharToTextLine(&w_ammo, *(s++));

  //jff 2/16/98 initialize health widget
  strcpy(hud_healthstr,"HEL ");
  s = hud_healthstr;
  while (*s)
    HUlib_addCharToTextLine(&w_health, *(s++));

  //jff 2/16/98 initialize armor widget
  strcpy(hud_armorstr,"ARM ");
  s = hud_armorstr;
  while (*s)
    HUlib_addCharToTextLine(&w_armor, *(s++));

  //jff 2/17/98 initialize weapons widget
  strcpy(hud_weapstr,"WEA ");
  s = hud_weapstr;
  while (*s)
    HUlib_addCharToTextLine(&w_weapon, *(s++));

  //jff 2/17/98 initialize keys widget
  if (!deathmatch) //jff 3/17/98 show frags in deathmatch mode
    strcpy(hud_keysstr,"KEY ");
  else
    strcpy(hud_keysstr,"FRG ");
  s = hud_keysstr;
  while (*s)
    HUlib_addCharToTextLine(&w_keys, *(s++));

  //jff 2/17/98 initialize graphic keys widget
  strcpy(hud_gkeysstr," ");
  s = hud_gkeysstr;
  while (*s)
    HUlib_addCharToTextLine(&w_gkeys, *(s++));

  //jff 2/17/98 initialize kills/items/secret widget
  strcpy(hud_monsecstr,"STS ");
  s = hud_monsecstr;
  while (*s)
    HUlib_addCharToTextLine(&w_monsec, *(s++));

  // create the chat widget
  HUlib_initIText
  (
    &w_chat,
    HU_INPUTX,
    HU_INPUTY,
    hu_font,
    HU_FONTSTART,
    hudcolor_chat,
    VPT_NONE,
    &chat_on
  );

  // create the inputbuffer widgets, one per player
  for (i=0 ; i<MAXPLAYERS ; i++)
    HUlib_initIText
    (
      &w_inputbuffer[i],
      0,
      0,
      0,
      0,
      hudcolor_chat,
      VPT_NONE,
      &always_off
    );

  // now allow the heads-up display to run
  headsupactive = true;

  HU_MoveHud(true);
}

void HU_NextHud(void)
{
  hud_active = (hud_active + 1) % 4;    // cycle hud_active
  if (!hud_active)                      //jff 3/4/98 add distributed
  {
    hud_distributed = !hud_distributed; // to cycle
  }
}

//
// HU_MoveHud()
//
// Move the HUD display from distributed to compact mode or vice-versa
//
// Passed nothing, returns nothing
//
//jff 3/9/98 create this externally callable to avoid glitch
// when menu scatter's HUD due to delay in change of position
//
void HU_MoveHud(int force)
{
  typedef struct hud_coords_s
  {
    hu_textline_t *name;
    int x[2];
    int y[2];
    int flags[2];
  } hud_coords_t;

  static int ohud_distributed = -1;
  static int ohud_active = -1;
  static int health_y = -1;
  int i;

  typedef enum
  {
    HUD_GKEYS, HUD_AMMO, HUD_WEAPON, HUD_KEYS, HUD_MONSEC,
    HUD_HEALTH, HUD_ARMOR, HUD_HUDADD,
    HUD_HEALTH_BIG, HUD_ARMOR_BIG, HUD_HEALTH_MEDICT,HUD_ARMOR_ARMOR,
    HUD_MAX,
  } hud_elem_e;

  static hud_coords_t hud[HUD_MAX] =
  {
    {&w_gkeys,  {-1, -1},  {HU_KEYSY, HU_KEYSY_D},     
     {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_LEFT_BOTTOM}},

    {&w_ammo,   {HU_AMMOX,   HU_AMMOX_D},   {HU_AMMOY, HU_AMMOY_D},     
    {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_RIGHT_BOTTOM}},

    {&w_weapon, {HU_WEAPX,   HU_WEAPX_D},   {HU_WEAPY, HU_WEAPY_D},     
    {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_RIGHT_BOTTOM}},

    {&w_keys,   {HU_KEYSX,   HU_KEYSX_D},   {HU_KEYSY, HU_KEYSY_D},     
    {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_LEFT_BOTTOM}},

    {&w_monsec, {HU_MONSECX, HU_MONSECX_D}, {HU_MONSECY, HU_MONSECY_D}, 
    {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_LEFT_BOTTOM}},

    {&w_health, {HU_HEALTHX, HU_HEALTHX_D}, {HU_HEALTHY, HU_HEALTHY_D}, 
    {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_RIGHT_TOP}},

    {&w_armor,  {HU_ARMORX,  HU_ARMORX_D},  {HU_ARMORY, HU_ARMORY_D},   
     {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_RIGHT_TOP}},

    {&w_hudadd, {HU_HUDADDX, HU_HUDADDX_D}, {HU_HUDADDY, HU_HUDADDY_D}, 
     {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_LEFT_BOTTOM}},

    {&w_health_big, {-1, -1}, {-1, -1},
     {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_LEFT_BOTTOM}},

    {&w_armor_big, {-1, -1}, {-1, -1},
     {VPT_ALIGN_RIGHT_BOTTOM, VPT_ALIGN_RIGHT_BOTTOM}},

    {&w_health_medict, {-1, -1}, {-1, -1},
     {VPT_ALIGN_LEFT_BOTTOM, VPT_ALIGN_LEFT_BOTTOM}},

    {&w_armor_armor, {-1, -1}, {-1, -1},
     {VPT_ALIGN_RIGHT_BOTTOM, VPT_ALIGN_RIGHT_BOTTOM}},
  };

  //jff 3/4/98 move displays around on F5 changing hud_distributed
  if (force || hud_distributed != ohud_distributed || hud_active != ohud_active)
  {
    int y;
    int hudnum = hud_distributed * 4 + hud_active;

    // HUDs with icons for health and armor
    if (hudnum == 3 || hudnum == 7)
    {
      int start = (hudnum == 3 ? 4 : 8);
      hu_font_hud[0] = hu_font_hud[start + 0];
      hu_font_hud[1] = hu_font_hud[start + 1];
      hu_font_hud[2] = hu_font_hud[start + 2];
      hu_font_hud[3] = hu_font_hud[start + 3];
      if (hu_font_hud[0].lumpnum < 0 ||
          hu_font_hud[1].lumpnum < 0 ||
          hu_font_hud[2].lumpnum < 0 ||
          hu_font_hud[3].lumpnum < 0)
      {
        //no hud graphics?
        HU_NextHud();
        hudnum = hud_distributed * 4 + hud_active;
      }
    }

    if (hud[0].x[0] == -1)
    {
      int n, max = 999;
      char buf[16];
      for (i = 0; i < NUMAMMO; i++)
      {
        max = MAX(max, maxammo[i]);
      }
          
      n = SNPRINTF(buf, 10, "%d", max) - 3;
      hud[HUD_WEAPON].x[1] -= n * 10;
      hud[HUD_AMMO].x[1] -= n * 10;
    }

    hud[0].x[0] = HU_KEYSGX;
    hud[0].x[1] = HU_KEYSGX_D;

    if (health_y != -1)
    {
      // restore
      for (i = 0; i <= HUD_HUDADD; i++)
      {
        hud[i].y[1] += health_y;
      }
      health_y = -1;
    }

    if (hudnum == 3 || hudnum == 7)
    {
      health_y = 0;
      // max height for (health / no armor / green armor / blue armor) icons
      for (i = 0; i < 4; i++)
      {
        health_y = MAX(health_y, hu_font_hud[i].height);
      }
      // big digits
      for (i = 0; i < 10; i++)
      {
        health_y = MAX(health_y, hu_font_hud[i + '0' - HU_FONTSTART].height);
      }
      health_y += 2;

      for (i = 0; i <= HUD_HUDADD; i++)
      {
        hud[i].y[1] -= health_y;
      }

      for (i = 0; i < 2; i++)
      {
        hud[HUD_HEALTH_MEDICT].x[i] = HU_HUDX_LL + hu_font_hud[0].leftoffset;
        hud[HUD_HEALTH_MEDICT].y[i] = 200 - hu_font_hud[0].height + hu_font_hud[0].topoffset - 2;
        hud[HUD_HEALTH_BIG].x[i] = HU_HUDX_LL + hu_font_hud[0].width + 4 ;
        hud[HUD_HEALTH_BIG].y[i] = 200 - hu_font_hud['1'-HU_FONTSTART].height - 1;

        hud[HUD_ARMOR_ARMOR].x[i] = 320 - hu_font_hud[1].width + hu_font_hud[1].leftoffset - 2;
        hud[HUD_ARMOR_ARMOR].y[i] = 200 - hu_font_hud[1].height + hu_font_hud[1].topoffset - 2;
        hud[HUD_ARMOR_BIG].x[i] = HU_HUDX_LR + hu_font_hud[1].width + 4;
        hud[HUD_ARMOR_BIG].y[i] = 200 - hu_font_hud['1'-HU_FONTSTART].height - 1;
      }
    }

    for (i = 0; i < sizeof(hud) /  sizeof(hud[0]); i++)
    {
      int index = (hud_distributed ? 1 : 0);
      if (hudnum == 3 && hud_active == 3)
        index = 1;
      hud[i].name->x = hud[i].x[index];
      hud[i].name->y = hud[i].y[index];
      hud[i].name->flags = hud[i].flags[index];
    }

    y = 0;
    switch (hudnum)
    {
    case 1:
      y = w_weapon.y;
      break;
    case 2:
    case 3:
    case 6:
    case 7:
      y = w_monsec.y;
      break;
    case 5:
      y = 200;
      break;
    }

    if (hudadd_gamespeed || hudadd_leveltime)
    {
      y = y - 1 * HU_GAPY;
      w_hudadd.y = y;
    }
    
    if (traces_present)
    {
      for (i = 0; i < NUMTRACES; i++)
      {
        w_traces[i].y = y - (i + 1) * HU_GAPY;
      }
    }

    ohud_distributed = hud_distributed;
    ohud_active = hud_active;
  }
}

void HU_widget_build_ammo(void)
{
  int i;
  char *s;
  char ammostr[80];  //jff 3/8/98 allow plenty room for dehacked mods
  int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo];

  // do the hud ammo display
  // clear the widgets internal line
  HUlib_clearTextLine(&w_ammo);
  strcpy(hud_ammostr,"AMM ");
  if (weaponinfo[plr->readyweapon].ammo == am_noammo || fullammo == 0)
  { // special case for weapon with no ammo selected - blank bargraph + N/A
    strcat(hud_ammostr,"\x7f\x7f\x7f\x7f\x7f\x7f\x7f N/A");
    w_ammo.cm = CR_GRAY;
  }
  else
  {
    int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo];
    int ammopct = (100*ammo)/fullammo;
    int ammobars = ammopct/4;

    // build the numeric amount init string
    sprintf(ammostr,"%d/%d",ammo,fullammo);
    // build the bargraph string
    // full bargraph chars
    for (i=4;i<4+ammobars/4;)
      hud_ammostr[i++] = 123;
    // plus one last character with 0,1,2,3 bars
    switch(ammobars%4)
    {
    case 0:
      break;
    case 1:
      hud_ammostr[i++] = 126;
      break;
    case 2:
      hud_ammostr[i++] = 125;
      break;
    case 3:
      hud_ammostr[i++] = 124;
      break;
    }

    // pad string with blank bar characters
    while(i<4+7)
      hud_ammostr[i++] = 127;
    hud_ammostr[i] = '\0';
    strcat(hud_ammostr,ammostr);

    // set the display color from the percentage of total ammo held
    if (ammopct<ammo_red)
      w_ammo.cm = CR_RED;
    else if (ammopct<ammo_yellow)
      w_ammo.cm = CR_GOLD;
    else
      w_ammo.cm = CR_GREEN;
  }
  // transfer the init string to the widget
  s = hud_ammostr;
  while (*s)
    HUlib_addCharToTextLine(&w_ammo, *(s++));
}

void HU_widget_draw_ammo(void)
{
  // display the ammo widget every frame
  HUlib_drawTextLine(&w_ammo, false);
}

void HU_widget_build_health(void)
{
  int i;
  char *s;
  char healthstr[80];//jff
  int health = plr->health;
  int healthbars = health>100? 25 : health/4;

  // clear the widgets internal line
  HUlib_clearTextLine(&w_health);

  // build the numeric amount init string
  sprintf(healthstr,"%3d",health);
  // build the bargraph string
  // full bargraph chars
  for (i=4;i<4+healthbars/4;)
    hud_healthstr[i++] = 123;
  // plus one last character with 0,1,2,3 bars
  switch(healthbars%4)
  {
  case 0:
    break;
  case 1:
    hud_healthstr[i++] = 126;
    break;
  case 2:
    hud_healthstr[i++] = 125;
    break;
  case 3:
    hud_healthstr[i++] = 124;
    break;
  }
  // pad string with blank bar characters
  while(i<4+7)
    hud_healthstr[i++] = 127;
  hud_healthstr[i] = '\0';
  strcat(hud_healthstr,healthstr);

  // set the display color from the amount of health posessed
  if (health<health_red)
    w_health.cm = CR_RED;
  else if (health<health_yellow)
    w_health.cm = CR_GOLD;
  else if (health<=health_green)
    w_health.cm = CR_GREEN;
  else
    w_health.cm = CR_BLUE;

  // transfer the init string to the widget
  s = hud_healthstr;
  while (*s)
    HUlib_addCharToTextLine(&w_health, *(s++));
}

void HU_widget_draw_health(void)
{
  HUlib_drawTextLine(&w_health, false);
}

void HU_widget_build_health_big(void)
{
  char *s;
  char healthstr[80];//jff
  int health = plr->health;

  // transfer the graphic key text to the widget
  HUlib_clearTextLine(&w_health_medict);
  HUlib_addCharToTextLine(&w_health_medict, '!' + 0);

  // clear the widgets internal line
  HUlib_clearTextLine(&w_health_big);

  // build the numeric amount init string
  sprintf(healthstr,"%3d",health);

  // set the display color from the amount of health posessed
  if (!sts_always_red)
  {
    if (health<health_red)
      w_health_big.cm = CR_RED;
    else if (health<health_yellow)
      w_health_big.cm = CR_GOLD;
    else if (health<=health_green)
      w_health_big.cm = CR_GREEN;
    else
      w_health_big.cm = CR_BLUE2;
  }

  // transfer the init string to the widget
  s = healthstr;
  while (*s)
    HUlib_addCharToTextLine(&w_health_big, *(s++));
}

void HU_widget_draw_health_big(void)
{
  HUlib_drawTextLine(&w_health_medict, false);
  HUlib_drawTextLine(&w_health_big, false);
}

void HU_widget_build_armor(void)
{
  int i;
  char *s;
  char armorstr[80]; //jff
  int armor = plr->armorpoints;
  int armorbars = armor>100? 25 : armor/4;

  // clear the widgets internal line
  HUlib_clearTextLine(&w_armor);
  // build the numeric amount init string
  sprintf(armorstr,"%3d",armor);
  // build the bargraph string
  // full bargraph chars
  for (i=4;i<4+armorbars/4;)
    hud_armorstr[i++] = 123;
  // plus one last character with 0,1,2,3 bars
  switch(armorbars%4)
  {
  case 0:
    break;
  case 1:
    hud_armorstr[i++] = 126;
    break;
  case 2:
    hud_armorstr[i++] = 125;
    break;
  case 3:
    hud_armorstr[i++] = 124;
    break;
  }
  // pad string with blank bar characters
  while(i<4+7)
    hud_armorstr[i++] = 127;
  hud_armorstr[i] = '\0';
  strcat(hud_armorstr,armorstr);

  // set the display color from the amount of armor posessed
  if (armor<armor_red)
    w_armor.cm = CR_RED;
  else if (armor<armor_yellow)
    w_armor.cm = CR_GOLD;
  else if (armor<=armor_green)
    w_armor.cm = CR_GREEN;
  else
    w_armor.cm = CR_BLUE;

  // transfer the init string to the widget
  s = hud_armorstr;
  while (*s)
    HUlib_addCharToTextLine(&w_armor, *(s++));
}

void HU_widget_draw_armor(void)
{
  HUlib_drawTextLine(&w_armor, false);
}

void HU_widget_build_armor_big(void)
{
  char *s;
  char armorstr[80]; //jff
  int armor = plr->armorpoints;

  // transfer the graphic key text to the widget
  HUlib_clearTextLine(&w_armor_armor);
  HUlib_addCharToTextLine(&w_armor_armor, (char)('!' + plr->armortype + 1));

  // clear the widgets internal line
  HUlib_clearTextLine(&w_armor_big);
  // build the numeric amount init string
  sprintf(armorstr,"%d",armor);

  // set the display color from the amount of armor posessed
  if (!sts_always_red)
  {
    if (armor<armor_red)
      w_armor_big.cm = CR_RED;
    else if (armor<armor_yellow)
      w_armor_big.cm = CR_GOLD;
    else if (armor<=armor_green)
      w_armor_big.cm = CR_GREEN;
    else
      w_armor_big.cm = CR_BLUE2;
  }

  // transfer the init string to the widget
  s = armorstr;
  w_armor_big.x = w_armor_armor.x - hu_font_hud[plr->armortype + 1].leftoffset - 4;
  while (*s)
  {
    HUlib_addCharToTextLine(&w_armor_big, *s);
    w_armor_big.x -= hu_font_hud[*s - HU_FONTSTART].width;
    s++;
  }
}

void HU_widget_draw_armor_big(void)
{
  HUlib_drawTextLine(&w_armor_armor, false);
  HUlib_drawTextLine(&w_armor_big, false);
}


void HU_widget_build_weapon(void)
{
  int i;
  char *s;
  int w;
  int ammo,fullammo,ammopct;

  // clear the widgets internal line
  HUlib_clearTextLine(&w_weapon);
  i=4; hud_weapstr[i] = '\0';      //jff 3/7/98 make sure ammo goes away

  // do each weapon that exists in current gamemode
  for (w=0;w<=wp_supershotgun;w++) //jff 3/4/98 show fists too, why not?
  {
    int ok=1;
    //jff avoid executing for weapons that do not exist
    switch (gamemode)
    {
    case shareware:
      if (w>=wp_plasma && w!=wp_chainsaw)
        ok=0;
      break;
    case retail:
    case registered:
      if (w>=wp_supershotgun)
        ok=0;
      break;
    default:
    case commercial:
      break;
    }
    if (!ok) continue;

    ammo = plr->ammo[weaponinfo[w].ammo];
    fullammo = plr->maxammo[weaponinfo[w].ammo];
    ammopct=0;

    // skip weapons not currently posessed
    if (!plr->weaponowned[w])
      continue;

    ammopct = fullammo? (100*ammo)/fullammo : 100;

    // display each weapon number in a color related to the ammo for it
    hud_weapstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
    if (weaponinfo[w].ammo==am_noammo) //jff 3/14/98 show berserk on HUD
      hud_weapstr[i++] = plr->powers[pw_strength]? '0'+CR_GREEN : '0'+CR_GRAY;
    else if (ammopct<ammo_red)
      hud_weapstr[i++] = '0'+CR_RED;
    else if (ammopct<ammo_yellow)
      hud_weapstr[i++] = '0'+CR_GOLD;
    else
      hud_weapstr[i++] = '0'+CR_GREEN;
    hud_weapstr[i++] = '0'+w+1;
    hud_weapstr[i++] = ' ';
    hud_weapstr[i] = '\0';
  }

  // transfer the init string to the widget
  s = hud_weapstr;
  while (*s)
    HUlib_addCharToTextLine(&w_weapon, *(s++));
}

void HU_widget_draw_weapon(void)
{
  HUlib_drawTextLine(&w_weapon, false);
}

void HU_widget_build_keys(void)
{
  int i;
  int k;
  char *s;

  hud_keysstr[4] = '\0';    //jff 3/7/98 make sure deleted keys go away
  //jff add case for graphic key display
  if (!deathmatch)
  {
    i=0;
    hud_gkeysstr[i] = '\0'; //jff 3/7/98 init graphic keys widget string
    // build text string whose characters call out graphic keys from fontk
    for (k=0;k<6;k++)
    {
      // skip keys not possessed
      if (!plr->cards[k])
        continue;

      hud_gkeysstr[i++] = '!'+k;   // key number plus '!' is char for key
      hud_gkeysstr[i++] = ' ';     // spacing
      hud_gkeysstr[i++] = ' ';
    }
    hud_gkeysstr[i]='\0';
  }
  else // not possible in current code, unless deathmatching,
  {
    i=4;
    hud_keysstr[i] = '\0';  //jff 3/7/98 make sure deleted keys go away

    // if deathmatch, build string showing top four frag counts
    if (deathmatch) //jff 3/17/98 show frags, not keys, in deathmatch
    {
      int top1=-999,top2=-999,top3=-999,top4=-999;
      int idx1=-1,idx2=-1,idx3=-1,idx4=-1;
      int fragcount,m;
      char numbuf[32];

      // scan thru players
      for (k=0;k<MAXPLAYERS;k++)
      {
        // skip players not in game
        if (!playeringame[k])
          continue;

        fragcount = 0;
        // compute number of times they've fragged each player
        // minus number of times they've been fragged by them
        for (m=0;m<MAXPLAYERS;m++)
        {
          if (!playeringame[m]) continue;
          fragcount += (m!=k)?  players[k].frags[m] : -players[k].frags[m];
        }

        // very primitive sort of frags to find top four
        if (fragcount>top1)
        {
          top4=top3; top3=top2; top2 = top1; top1=fragcount;
          idx4=idx3; idx3=idx2; idx2 = idx1; idx1=k;
        }
        else if (fragcount>top2)
        {
          top4=top3; top3=top2; top2=fragcount;
          idx4=idx3; idx3=idx2; idx2=k;
        }
        else if (fragcount>top3)
        {
          top4=top3; top3=fragcount;
          idx4=idx3; idx3=k;
        }
        else if (fragcount>top4)
        {
          top4=fragcount;
          idx4=k;
        }
      }
      // if the biggest number exists, put it in the init string
      if (idx1>-1)
      {
        sprintf(numbuf,"%5d",top1);
        // make frag count in player's color via escape code
        hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
        hud_keysstr[i++] = '0'+plyrcoltran[idx1&3];
        s = numbuf;
        while (*s)
          hud_keysstr[i++] = *(s++);
      }
      // if the second biggest number exists, put it in the init string
      if (idx2>-1)
      {
        sprintf(numbuf,"%5d",top2);
        // make frag count in player's color via escape code
        hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
        hud_keysstr[i++] = '0'+plyrcoltran[idx2&3];
        s = numbuf;
        while (*s)
          hud_keysstr[i++] = *(s++);
      }
      // if the third biggest number exists, put it in the init string
      if (idx3>-1)
      {
        sprintf(numbuf,"%5d",top3);
        // make frag count in player's color via escape code
        hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
        hud_keysstr[i++] = '0'+plyrcoltran[idx3&3];
        s = numbuf;
        while (*s)
          hud_keysstr[i++] = *(s++);
      }
      // if the fourth biggest number exists, put it in the init string
      if (idx4>-1)
      {
        sprintf(numbuf,"%5d",top4);
        // make frag count in player's color via escape code
        hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
        hud_keysstr[i++] = '0'+plyrcoltran[idx4&3];
        s = numbuf;
        while (*s)
          hud_keysstr[i++] = *(s++);
      }
      hud_keysstr[i] = '\0';
    } //jff 3/17/98 end of deathmatch clause
    else // build alphabetical key display (not used currently)
    {
      // scan the keys
      for (k=0;k<6;k++)
      {
        // skip any not possessed by the displayed player's stats
        if (!plr->cards[k])
          continue;

        // use color escapes to make text in key's color
        hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
        switch(k)
        {
        case 0:
          hud_keysstr[i++] = '0'+CR_BLUE;
          hud_keysstr[i++] = 'B';
          hud_keysstr[i++] = 'C';
          hud_keysstr[i++] = ' ';
          break;
        case 1:
          hud_keysstr[i++] = '0'+CR_GOLD;
          hud_keysstr[i++] = 'Y';
          hud_keysstr[i++] = 'C';
          hud_keysstr[i++] = ' ';
          break;
        case 2:
          hud_keysstr[i++] = '0'+CR_RED;
          hud_keysstr[i++] = 'R';
          hud_keysstr[i++] = 'C';
          hud_keysstr[i++] = ' ';
          break;
        case 3:
          hud_keysstr[i++] = '0'+CR_BLUE;
          hud_keysstr[i++] = 'B';
          hud_keysstr[i++] = 'S';
          hud_keysstr[i++] = ' ';
          break;
        case 4:
          hud_keysstr[i++] = '0'+CR_GOLD;
          hud_keysstr[i++] = 'Y';
          hud_keysstr[i++] = 'S';
          hud_keysstr[i++] = ' ';
          break;
        case 5:
          hud_keysstr[i++] = '0'+CR_RED;
          hud_keysstr[i++] = 'R';
          hud_keysstr[i++] = 'S';
          hud_keysstr[i++] = ' ';
          break;
        }
        hud_keysstr[i]='\0';
      }
    }
  }

  HUlib_clearTextLine(&w_keys);      // clear the widget strings

  // transfer the built string (frags or key title) to the widget
  s = hud_keysstr; //jff 3/7/98 display key titles/key text or frags
  while (*s)
    HUlib_addCharToTextLine(&w_keys, *(s++));

  //jff 3/17/98 show graphic keys in non-DM only
  if (!deathmatch) //jff 3/7/98 display graphic keys
  {
    // clear the widget strings
    HUlib_clearTextLine(&w_gkeys);
    // transfer the graphic key text to the widget
    s = hud_gkeysstr;
    while (*s)
      HUlib_addCharToTextLine(&w_gkeys, *(s++));
  }
}

void HU_widget_draw_keys(void)
{
  HUlib_drawTextLine(&w_keys, false);
  if (!deathmatch)
  {
    HUlib_drawTextLine(&w_gkeys, false);
  }
}

void HU_widget_build_monsec(void)
{
  int i;
  char *s;

  //e6y
  char allkills[200], allsecrets[200];
  int playerscount;
  int  fullkillcount, fullitemcount, fullsecretcount;
  int color, killcolor, itemcolor, secretcolor;

  // clear the internal widget text buffer
  HUlib_clearTextLine(&w_monsec);
  if (!hudadd_smarttotals || deathmatch)
  {
    sprintf
      (
      hud_monsecstr,
      "STS \x1b\x36K \x1b\x33%d \x1b\x36M \x1b\x33%d \x1b\x37I \x1b\x33%d/%d \x1b\x35S \x1b\x33%d/%d",
      plr->killcount,totallive,
      plr->itemcount,totalitems,
      plr->secretcount,totalsecret
      );
  }
  else
  {
    int allkills_len = 0;
    int allsecrets_len = 0;

    playerscount = 0;
    fullkillcount = 0;
    fullitemcount = 0;
    fullsecretcount = 0;
    for (i=0 ; i<MAXPLAYERS ; i++)
    {
      if (playeringame[i])
      {
        color = i==displayplayer?0x33:0x32;
        if (playerscount==0)
        {
          allkills_len = sprintf(allkills, "\x1b%c%d", color, players[i].killcount - players[i].resurectedkillcount);
          allsecrets_len = sprintf(allsecrets, "\x1b%c%d", color, players[i].secretcount);
        }
        else
        {
          if (allkills_len >= 0 && allsecrets_len >=0)
          {
            allkills_len += sprintf(&allkills[allkills_len], "\x1b%c+%d", color, players[i].killcount - players[i].resurectedkillcount);
            allsecrets_len += sprintf(&allsecrets[allsecrets_len], "\x1b%c+%d", color, players[i].secretcount);
          }
        }
        playerscount++;
        fullkillcount += players[i].killcount - players[i].resurectedkillcount;
        fullitemcount += players[i].itemcount;
        fullsecretcount += players[i].secretcount;
      }
    }
    killcolor = fullkillcount==totalkills?0x37:0x35;
    secretcolor = fullsecretcount==totalsecret?0x37:0x35;
    itemcolor = fullitemcount==totalitems?0x37:0x35;
    if (playerscount<2)
    {
      sprintf
        (
        hud_monsecstr,
        "STS \x1b\x36K \x1b%c%d/%d \x1b\x36I \x1b%c%d/%d \x1b\x36S \x1b%c%d/%d",
        killcolor, fullkillcount,totalkills,
        itemcolor,plr->itemcount,totalitems,
        secretcolor, fullsecretcount,totalsecret
        );
    }
    else
    {
      sprintf
        (
        hud_monsecstr,
        "STS \x1b\x36K %s \x1b%c%d/%d \x1b\x36I \x1b%c%d/%d \x1b\x36S %s \x1b%c%d/%d",
        allkills,killcolor,fullkillcount,totalkills,
        itemcolor,plr->itemcount,totalitems,
        allsecrets,secretcolor,fullsecretcount,totalsecret
        );
    }
  }

  // transfer the init string to the widget
  s = hud_monsecstr;
  while (*s)
    HUlib_addCharToTextLine(&w_monsec, *(s++));
}

void HU_widget_draw_monsec(void)
{
  HUlib_drawTextLine(&w_monsec, false);
}

void HU_widget_build_hudadd(void)
{
  char *s;
  hud_add[0] = 0;
  if (hudadd_gamespeed)
    sprintf(hud_add,"\x1b\x32speed \x1b\x33%.2d ", realtic_clock_rate);
  if ((hudadd_leveltime) || (demoplayback && hudadd_demotime))
  {
    static char demo_len_null[1]={0};
    char *demo_len = demoplayback && hudadd_demotime ? demo_len_st : demo_len_null;
    if (totalleveltimes)
      sprintf(hud_add+strlen(hud_add),"\x1b\x32time \x1b\x35%d:%02d%s \x1b\x33%d:%05.2f ", 
      (totalleveltimes+leveltime)/35/60, ((totalleveltimes+leveltime)%(60*35))/35, demo_len, 
      leveltime/35/60, (float)(leveltime%(60*35))/35);
    else
      sprintf(hud_add+strlen(hud_add),"\x1b\x32time \x1b\x33%d:%05.2f%s ", 
      leveltime/35/60, (float)(leveltime%(60*35))/35, demo_len);
  }
  HUlib_clearTextLine(&w_hudadd);
  s = hud_add;
  while (*s)
    HUlib_addCharToTextLine(&w_hudadd, *(s++));
}

void HU_widget_draw_hudadd(void)
{
  HUlib_drawTextLine(&w_hudadd, false);
}

//
// HU_Drawer()
//
// Draw all the pieces of the heads-up display
//
// Passed nothing, returns nothing
//
void HU_Drawer(void)
{
  char *s;
  player_t *plr;
  int doit;
  //jff 3/4/98 speed update up for slow systems
  //e6y: speed update for uncapped framerate
  static dboolean needupdate = false;
  if (realframe) needupdate = !needupdate;
  doit = needupdate;

  plr = &players[displayplayer];         // killough 3/7/98
  // draw the automap widgets if automap is displayed
  if (automapmode & am_active)
  {
    // map title
    HUlib_drawTextLine(&w_title, false);

    //jff 2/16/98 output new coord display
    // x-coord
    if (map_point_coordinates)
    {

      //e6y: speedup
      if (!realframe)
      {
        HUlib_drawTextLine(&w_coordx, false);
        HUlib_drawTextLine(&w_coordy, false);
        HUlib_drawTextLine(&w_coordz, false);
      }
      else
      {
        sprintf(hud_coordstrx,"X: %-5d", (plr->mo->x)>>FRACBITS);
        HUlib_clearTextLine(&w_coordx);
        s = hud_coordstrx;
        while (*s)
          HUlib_addCharToTextLine(&w_coordx, *(s++));
        HUlib_drawTextLine(&w_coordx, false);

        //jff 3/3/98 split coord display into x,y,z lines
        // y-coord
        sprintf(hud_coordstry,"Y: %-5d", (plr->mo->y)>>FRACBITS);
        HUlib_clearTextLine(&w_coordy);
        s = hud_coordstry;
        while (*s)
          HUlib_addCharToTextLine(&w_coordy, *(s++));
        HUlib_drawTextLine(&w_coordy, false);

        //jff 3/3/98 split coord display into x,y,z lines
        //jff 2/22/98 added z
        // z-coord
        sprintf(hud_coordstrz,"Z: %-5d", (plr->mo->z)>>FRACBITS);
        HUlib_clearTextLine(&w_coordz);
        s = hud_coordstrz;
        while (*s)
          HUlib_addCharToTextLine(&w_coordz, *(s++));
        HUlib_drawTextLine(&w_coordz, false);
      }
    }

    if (map_level_stat)
    {
      static char str[32];
      int time = leveltime / TICRATE;
      int ttime = (totalleveltimes + leveltime) / TICRATE;

      sprintf(str, "Monsters: \x1b%c%d/%d", '0' + hudcolor_mapstat_value,
        players[consoleplayer].killcount - players[consoleplayer].resurectedkillcount,
        totalkills);
      HUlib_clearTextLine(&w_map_monsters);
      s = str;
      while (*s)
        HUlib_addCharToTextLine(&w_map_monsters, *(s++));
      HUlib_drawTextLine(&w_map_monsters, false);

      sprintf(str, "Secrets: \x1b%c%d/%d", '0' + hudcolor_mapstat_value,
        players[consoleplayer].secretcount, totalsecret);
      HUlib_clearTextLine(&w_map_secrets);
      s = str;
      while (*s)
        HUlib_addCharToTextLine(&w_map_secrets, *(s++));
      HUlib_drawTextLine(&w_map_secrets, false);

      sprintf(str, "Items: \x1b%c%d/%d", '0' + hudcolor_mapstat_value,
        players[consoleplayer].itemcount, totalitems);
      HUlib_clearTextLine(&w_map_items);
      s = str;
      while (*s)
        HUlib_addCharToTextLine(&w_map_items, *(s++));
      HUlib_drawTextLine(&w_map_items, false);

      sprintf(str, "%02d:%02d:%02d", time/3600, (time%3600)/60, time%60);
      HUlib_clearTextLine(&w_map_time);
      s = str;
      while (*s)
        HUlib_addCharToTextLine(&w_map_time, *(s++));
      HUlib_drawTextLine(&w_map_time, false);

      if (totalleveltimes > 0)
      {
        sprintf(str, "%02d:%02d:%02d", ttime/3600, (ttime%3600)/60, ttime%60);
        HUlib_clearTextLine(&w_map_totaltime);
        s = str;
        while (*s)
          HUlib_addCharToTextLine(&w_map_totaltime, *(s++));
        HUlib_drawTextLine(&w_map_totaltime, false);
      }
    }
  }

  // draw the weapon/health/ammo/armor/kills/keys displays if optioned
  //jff 2/17/98 allow new hud stuff to be turned off
  // killough 2/21/98: really allow new hud stuff to be turned off COMPLETELY
  if
  (
    hud_active>0 &&                  // hud optioned on
    hud_displayed &&                 // hud on from fullscreen key
    viewheight==SCREENHEIGHT &&      // fullscreen mode is active
    !(automapmode & am_active)       // automap is not active
  )
  {
    HU_MoveHud(false);                  // insure HUD display coords are correct

    if (doit)            //jff 8/7/98 update every time, avoid lag in update
    {
      if (hud_active == 3)
      {
        HU_widget_build_health_big();
        HU_widget_build_armor_big();
      }
      else
      {
        HU_widget_build_health();
        HU_widget_build_armor();
      }

      if (hud_active != 3 || hud_distributed == 1)
      {
        HU_widget_build_ammo();
        HU_widget_build_weapon();

        if (hud_active > 1)
        {
          HU_widget_build_keys();
          HU_widget_build_monsec();
        }

        if (hudadd_gamespeed || hudadd_leveltime)
        {
          HU_widget_build_hudadd();
        }
      }
    }

    if (hud_active == 3)
    {
      HU_widget_draw_health_big();
      HU_widget_draw_armor_big();
    }
    else
    {
      HU_widget_draw_health();
      HU_widget_draw_armor();
    }

    if (hud_active != 3 || hud_distributed == 1)
    {
      HU_widget_draw_ammo();
      HU_widget_draw_weapon();
      if (hud_active > 1)
      {
        HU_widget_draw_keys();
        HU_widget_draw_monsec();
      }
      if (hudadd_gamespeed || hudadd_leveltime)
      {
        HU_widget_draw_hudadd();
      }
    }

    //e6y
    if (traces_present)
    {
      int k, num = 0;
      for(k = 0; k < NUMTRACES; k++)
      {
        if (traces[k].count)
        {
          if (realframe)
          {
            if (traces[k].ApplyFunc)
              traces[k].ApplyFunc(k);

            HUlib_clearTextLine(&w_traces[num]);
            s = traces[k].hudstr;
            while (*s)
              HUlib_addCharToTextLine(&w_traces[num], *(s++));

            if (traces[k].ResetFunc)
              traces[k].ResetFunc(k);
          }
          HUlib_drawTextLine(&w_traces[num], false);
          num++;
        }
      }
    }

  }

  //jff 3/4/98 display last to give priority
  HU_Erase(); // jff 4/24/98 Erase current lines before drawing current
              // needed when screen not fullsize

  //jff 4/21/98 if setup has disabled message list while active, turn it off
  if (hud_msg_lines<=1)
    message_list = false;

  // if the message review not enabled, show the standard message widget
  if (!message_list)
    HUlib_drawSText(&w_message);

  //e6y
  if (messagecenter_counter)
    HUlib_drawTextLine(&w_centermsg, false);

  // if the message review is enabled show the scrolling message review
  if (hud_msg_lines>1 && message_list)
    HUlib_drawMText(&w_rtext);

  // display the interactive buffer for chat entry
  HUlib_drawIText(&w_chat);
}

//
// HU_Erase()
//
// Erase hud display lines that can be trashed by small screen display
//
// Passed nothing, returns nothing
//
void HU_Erase(void)
{
  // erase the message display or the message review display
  if (!message_list)
    HUlib_eraseSText(&w_message);
  else
    HUlib_eraseMText(&w_rtext);

  //e6y
  if (messagecenter_counter)
    HUlib_eraseTextLine(&w_centermsg);

  // erase the interactive text buffer for chat entry
  HUlib_eraseIText(&w_chat);

  // erase the automap title
  HUlib_eraseTextLine(&w_title);
}

//
// HU_Ticker()
//
// Update the hud displays once per frame
//
// Passed nothing, returns nothing
//
static dboolean bsdown; // Is backspace down?
static int bscounter;

void HU_Ticker(void)
{
  int i, rc;
  char c;

  // tick down message counter if message is up
  if (message_counter && !--message_counter)
  {
    message_on = false;
    message_nottobefuckedwith = false;
  }
  if (bsdown && bscounter++ > 9) {
    HUlib_keyInIText(&w_chat, (unsigned char)key_backspace);
    bscounter = 8;
  }

  // if messages on, or "Messages Off" is being displayed
  // this allows the notification of turning messages off to be seen
  if (showMessages || message_dontfuckwithme)
  {
    // display message if necessary
    if ((plr->message && !message_nottobefuckedwith)
        || (plr->message && message_dontfuckwithme))
    {
      //post the message to the message widget
      HUlib_addMessageToSText(&w_message, 0, plr->message);
      //jff 2/26/98 add message to refresh text widget too
      HUlib_addMessageToMText(&w_rtext, 0, plr->message);

      // clear the message to avoid posting multiple times
      plr->message = 0;
      // note a message is displayed
      message_on = true;
      // start the message persistence counter
      message_counter = HU_MSGTIMEOUT;
      // transfer "Messages Off" exception to the "being displayed" variable
      message_nottobefuckedwith = message_dontfuckwithme;
      // clear the flag that "Messages Off" is being posted
      message_dontfuckwithme = 0;
    }
  }
  //e6y
  if (messagecenter_counter)
    messagecenter_counter--;
  if (/*showMessages && */plr->centermessage)
  {
    const char *s = plr->centermessage; 
    HUlib_clearTextLine(&w_centermsg);
    while (*s)
    {
      HUlib_addCharToTextLine(&w_centermsg, *(s++));
    }
    HUlib_setTextXCenter(&w_centermsg);
    plr->centermessage = NULL;
    messagecenter_counter = HU_MSGCENTERTIMEOUT;

    S_StartSound(NULL, sfx_secret);
  }

  // check for incoming chat characters
  if (netgame)
  {
    for (i=0; i<MAXPLAYERS; i++)
    {
      if (!playeringame[i])
        continue;
      if (i != consoleplayer
          && (c = players[i].cmd.chatchar))
      {
        if (c <= HU_BROADCAST)
          chat_dest[i] = c;
        else
        {
          if (c >= 'a' && c <= 'z')
            c = (char) shiftxform[(unsigned char) c];
          rc = HUlib_keyInIText(&w_inputbuffer[i], c);
          if (rc && c == KEYD_ENTER)
          {
            if (w_inputbuffer[i].l.len
                && (chat_dest[i] == consoleplayer+1
                || chat_dest[i] == HU_BROADCAST))
            {
              HUlib_addMessageToSText(&w_message,
                                      player_names[i],
                                      w_inputbuffer[i].l.l);

              message_nottobefuckedwith = true;
              message_on = true;
              message_counter = HU_MSGTIMEOUT;
              if ( gamemode == commercial )
                S_StartSound(0, sfx_radio);
              else
                S_StartSound(0, sfx_tink);
            }
            HUlib_resetIText(&w_inputbuffer[i]);
          }
        }
        players[i].cmd.chatchar = 0;
      }
    }
  }
}

#define QUEUESIZE   128

static char chatchars[QUEUESIZE];
static int  head = 0;
static int  tail = 0;

//
// HU_queueChatChar()
//
// Add an incoming character to the circular chat queue
//
// Passed the character to queue, returns nothing
//
static void HU_queueChatChar(char c)
{
  if (((head + 1) & (QUEUESIZE-1)) == tail)
  {
    plr->message = HUSTR_MSGU;
  }
  else
  {
    chatchars[head] = c;
    head = (head + 1) & (QUEUESIZE-1);
  }
}

//
// HU_dequeueChatChar()
//
// Remove the earliest added character from the circular chat queue
//
// Passed nothing, returns the character dequeued
//
char HU_dequeueChatChar(void)
{
  char c;

  if (head != tail)
  {
    c = chatchars[tail];
    tail = (tail + 1) & (QUEUESIZE-1);
  }
  else
  {
    c = 0;
  }
  return c;
}

//
// HU_Responder()
//
// Responds to input events that affect the heads up displays
//
// Passed the event to respond to, returns true if the event was handled
//
dboolean HU_Responder(event_t *ev)
{

  static char   lastmessage[HU_MAXLINELENGTH+1];
  const char*   macromessage; // CPhipps - const char*
  dboolean   eatkey = false;
  static dboolean  shiftdown = false;
  static dboolean  altdown = false;
  unsigned char   c;
  int     i;
  int     numplayers;

  static int    num_nobrainers = 0;

  numplayers = 0;
  for (i=0 ; i<MAXPLAYERS ; i++)
    numplayers += playeringame[i];

  if (ev->data1 == key_shift)
  {
    shiftdown = ev->type == ev_keydown;
    return false;
  }
  else if (ev->data1 == key_alt)
  {
    altdown = ev->type == ev_keydown;
    return false;
  }
  else if (ev->data1 == key_backspace)
  {
    bsdown = ev->type == ev_keydown;
    bscounter = 0;
  }

  if (ev->type != ev_keydown)
    return false;

  if (!chat_on)
  {
    if (ev->data1 == key_enter)                                 // phares
    {
#ifndef INSTRUMENTED  // never turn on message review if INSTRUMENTED defined
      if (hud_msg_lines>1)  // it posts multi-line messages that will trash
      {
        if (message_list) HU_Erase(); //jff 4/28/98 erase behind messages
        message_list = !message_list; //jff 2/26/98 toggle list of messages
      }
#endif
      if (!message_list)              // if not message list, refresh message
      {
        message_on = true;
        message_counter = HU_MSGTIMEOUT;
      }
      eatkey = true;
    }//jff 2/26/98 no chat if message review is displayed
    // killough 10/02/98: no chat if demo playback
    // no chat in -solo-net mode
    else if (!demoplayback && !message_list && netgame && numplayers > 1)
    {
      if (ev->data1 == key_chat)
    {
      eatkey = chat_on = true;
      HUlib_resetIText(&w_chat);
      HU_queueChatChar(HU_BROADCAST);
    }
    else if (numplayers > 2)
    {
      for (i=0; i<MAXPLAYERS ; i++)
      {
        if (ev->data1 == destination_keys[i])
        {
          if (playeringame[i] && i!=consoleplayer)
          {
            eatkey = chat_on = true;
            HUlib_resetIText(&w_chat);
            HU_queueChatChar((char)(i+1));
            break;
          }
          else if (i == consoleplayer)
          {
            num_nobrainers++;
            if (num_nobrainers < 3)
                plr->message = HUSTR_TALKTOSELF1;
            else if (num_nobrainers < 6)
                plr->message = HUSTR_TALKTOSELF2;
            else if (num_nobrainers < 9)
                plr->message = HUSTR_TALKTOSELF3;
            else if (num_nobrainers < 32)
                plr->message = HUSTR_TALKTOSELF4;
            else
                plr->message = HUSTR_TALKTOSELF5;
          }
        }
      }
    }
    }
  }//jff 2/26/98 no chat functions if message review is displayed
  else if (!message_list)
  {
    c = ev->data1;
    // send a macro
    if (altdown)
    {
      c = c - '0';
      if (c > 9)
        return false;
      macromessage = chat_macros[c];

      // kill last message with a '\n'
        HU_queueChatChar((char)key_enter); // DEBUG!!!                // phares

      // send the macro message
      while (*macromessage)
        HU_queueChatChar(*macromessage++);
      HU_queueChatChar((char)key_enter);                            // phares

      // leave chat mode and notify that it was sent
      chat_on = false;
      strcpy(lastmessage, chat_macros[c]);
      plr->message = lastmessage;
      eatkey = true;
    }
    else
    {
      if (shiftdown || (c >= 'a' && c <= 'z'))
        c = shiftxform[c];
      eatkey = HUlib_keyInIText(&w_chat, c);
      if (eatkey)
        HU_queueChatChar(c);

      if (c == key_enter)                                     // phares
      {
        chat_on = false;
        if (w_chat.l.len)
        {
          strcpy(lastmessage, w_chat.l.l);
          plr->message = lastmessage;
        }
      }
      else if (c == key_escape)                               // phares
        chat_on = false;
    }
  }
  return eatkey;
}
